﻿using System.Collections.Generic;
using System.Collections.Specialized;
using System.Transactions;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.SessionState;
using Moq;
using NUnit.Framework;
using TeamsPortal.Configuration;
using TeamsPortal.Configuration.Routing;
using TeamsPortal.DataAccess;
using TeamsPortal.ServiceClient;
using TeamsPortal.Tests.Fakes;
using TeamsPortal.Web.Controllers;
using TeamsPortal.Web.Extensions;
using TeamsPortal.Web.State;

namespace TeamsPortal.Tests.Controllers
{
	[TestFixture]
	public class ForumsControllerTest
	{
        private TransactionScope _scope;

        [SetUp]
        public void Init()
        {
            _scope = new TransactionScope();
        }

        [TearDown]
        public void Cleanup()
        {
            _scope.Dispose();
        }


        //todo: remove when mock introducing is done
        public static Forum GetAForum()
		{
			Forum forum = null;
			var controller = new ForumsController(
                new TopicsServiceClient(new MessagesServiceClient()), new ForumsServiceClient());
			controller.ControllerContext = new FakeControllerContext(controller, "http://localhost");

			controller.List();
			List<ForumCategory> forumCategoryList = (List<ForumCategory>)controller.ViewData.Model;

			if (forumCategoryList.Count == 0)
			{
				Assert.Inconclusive("There are no forum categories to perform this test.");
			}

			foreach (ForumCategory category in forumCategoryList)
			{
				foreach (Forum f in category.Forums)
				{
					forum = f;
					break;
				}
				if (forum != null)
				{
					break;
				}
			}
			if (forum == null)
			{
				Assert.Inconclusive("There are no forums to perform this test.");
			}

			return forum;
		}


		public static ForumCategory GetACategory()
		{
            return new ForumCategory(1, "MockCategory");
		}

		public static SessionStateItemCollection GetSessionWithTestUser()
		{
			SessionStateItemCollection sessionItems = new SessionStateItemCollection();
			User user = ServicesTests.GetTestUser();
			sessionItems["User"] = new UserState(user, AuthenticationProvider.Facebook);
			return sessionItems;
		}

        public static SessionStateItemCollection GetSessionWithTestUser(User user)
        {
            SessionStateItemCollection sessionItems = new SessionStateItemCollection();
            sessionItems["User"] = new UserState(user, AuthenticationProvider.Facebook);
            return sessionItems;
        }

		[Test]
		public void Forums_List_Detail()
        {
            var mockTopicServiceClient = new Mock<ITopicsServiceClient>();
            const string forumShortName = "ShortName";
            var mockForumServiceClient = new Mock<IForumsServiceClient>();
            mockForumServiceClient
                .Setup(m => m.Get(forumShortName))
                .Returns(new Forum() { ShortName = forumShortName });
            var controller = new ForumsController(mockTopicServiceClient.Object, mockForumServiceClient.Object);

            if (controller.Config.UI.DefaultForumSort == ForumSort.LatestActivity)
            {
                mockTopicServiceClient
                    .Setup(m => m.GetLatest(It.IsAny<int>(), 1, controller.Config.UI.TopicsPerPage - 1, It.IsAny<UserRole?>(), default(int)))
                    .Returns(It.IsAny<List<Topic>>);
            }
            else
            {
                mockTopicServiceClient
                    .Setup(m=>m.GetByForum(It.IsAny<int>(), 1, controller.Config.UI.TopicsPerPage - 1, It.IsAny<UserRole?>()))
                    .Returns(It.IsAny<List<Topic>>);
            }

            controller.ControllerContext = new FakeControllerContext(controller);
            //Forum forum = GetAForum();

            controller.ViewData = new ViewDataDictionary();

            controller.Detail(forumShortName, 1);

            Forum forum = (Forum)controller.ViewData.Model;
            Assert.IsNotNull(forum);

            mockTopicServiceClient.VerifyAll();
            mockForumServiceClient.VerifyAll();
        }

		[Test]
		public void Forums_Unanswered()
		{
            var mockTopicServiceClient = new Mock<ITopicsServiceClient>();
            mockTopicServiceClient
                .Setup(m => m.GetUnanswered())
                .Returns(new List<Topic>());

			ForumsController controller = new ForumsController(mockTopicServiceClient.Object, null);
			controller.ViewData = new ViewDataDictionary();

			controller.ListAllUnansweredTopics();
			Assert.IsNotNull(controller.ViewData.Model);

            mockTopicServiceClient.VerifyAll();
		}

        [Test]
        public void Forums_UnansweredByShortName()
        {
            const string shortName = "asdasdas";

            var mockTopicServiceClient = new Mock<ITopicsServiceClient>();
            mockTopicServiceClient
                .Setup(m => m.GetUnanswered(It.IsAny<int>(), It.IsAny<UserRole?>()))
                .Returns(new List<Topic>());
            var mockForumServiceClient = new Mock<IForumsServiceClient>();
            mockForumServiceClient
                .Setup(m => m.Get(shortName))
                .Returns(new Forum() { ShortName = shortName });

            var controller = new ForumsController(mockTopicServiceClient.Object, mockForumServiceClient.Object);
            controller.ViewData = new ViewDataDictionary();

            controller.ListUnansweredTopics(shortName);
            Assert.IsNotNull(controller.ViewData.Model);

            mockTopicServiceClient.VerifyAll();
            mockForumServiceClient.VerifyAll();
        }
		
		[Test]
		public void Forum_LatestAllTopics()
		{
            var mockTopicServiceClient = new Mock<ITopicsServiceClient>();
            mockTopicServiceClient
                .Setup(m => m.GetLatest())
                .Returns(It.IsAny<List<Topic>>());

            var controller = new ForumsController(mockTopicServiceClient.Object, null);
			controller.ControllerContext = new FakeControllerContext(controller);
            ActionResult result = controller.LatestAllTopics();

			Assert.IsInstanceOf<ViewResult>(result);
		}

		[Test]
		public void Forum_Edit()
		{
            const string forumShortName = "shortName";
            var forum = new Forum() { ShortName = forumShortName };
            var forumService = new Mock<IForumsServiceClient>();
            forumService
                .Setup(m => m.Get(forumShortName))
                .Returns(forum);
            forumService
                .Setup(m => m.GetCategories())
                .Returns(new List<ForumCategory>());
            var controller = new ForumsController(null, forumService.Object);
            var mockUserServiceClient = new Mock<IUsersServiceClient>();
            mockUserServiceClient
                .Setup(m => m.GetRoles())
                .Returns(new Dictionary<UserRole, string>());
            controller.UsersServiceClient = mockUserServiceClient.Object;

			controller.ControllerContext = new FakeControllerContext(controller, "http://localhost", null, null, new NameValueCollection(), new NameValueCollection(), new HttpCookieCollection(), ForumsControllerTest.GetSessionWithTestUser());

            controller.Edit(forumShortName);
			forum = (Forum)controller.ViewData.Model;

			ActionResult result = controller.Edit(forum.ShortName, forum);
            Assert.IsInstanceOf<RedirectToRouteResult>(result);

            forumService.VerifyAll();
		}

		[Test]
		public void Forum_Delete_English()
		{
            var forum = new Forum()
            {
                Name = "Unit test forum",
                Category = new ForumCategory(1, "TestCategory"), //GetACategory();
                ShortName = "Short",
            };
            forum.Description = forum.Name + "... description.";

            var mockForumsServiceClient = new Mock<IForumsServiceClient>();
            mockForumsServiceClient.Setup(m => m.Delete(forum.ShortName));
            
            ForumsController controller = new ForumsController(null, mockForumsServiceClient.Object);
            //todo: check whether it's necessary
			controller.ControllerContext = new FakeControllerContext(controller, "http://localhost", null, null, new NameValueCollection(), new NameValueCollection(), new HttpCookieCollection(), ForumsControllerTest.GetSessionWithTestUser());

			var result = controller.Delete(forum.ShortName);
            Assert.IsInstanceOf<RedirectToRouteResult>(result);

            mockForumsServiceClient.VerifyAll();
		}

        [Test]
        public void Forum_Add_English()
        {
            var forum = new Forum()
            {
                Name = "Unit test forum",
                Category = new ForumCategory(1, "TestCategory"), //GetACategory();
                ShortName = "Short",
            };
            forum.Description = forum.Name + "... description.";

            var mockForumsServiceClient = new Mock<IForumsServiceClient>();
            mockForumsServiceClient.Setup(m => m.Add(forum, It.IsAny<int>()));

            ForumsController controller = new ForumsController(null, mockForumsServiceClient.Object);
            //todo: check whether it's necessary
            controller.ControllerContext = new FakeControllerContext(controller, "http://localhost", null, null, new NameValueCollection(), new NameValueCollection(), new HttpCookieCollection(), ForumsControllerTest.GetSessionWithTestUser());

            var result = controller.Add(forum);
            Assert.IsInstanceOf<RedirectToRouteResult>(result);

            mockForumsServiceClient.VerifyAll();
        }

		[Test]
		public void Forum_Add_Delete_Thai()
		{
            var forum = new Forum();
            int userId = 10;
            User user = new User(userId, "UserName");
            SessionStateItemCollection state = ForumsControllerTest.GetSessionWithTestUser(user);
            var mockForumClient = new Mock<IForumsServiceClient>();
            mockForumClient.Setup(m => m.Add(forum, userId));
            ForumsController controller = new ForumsController(null, mockForumClient.Object);
			controller.ControllerContext = new FakeControllerContext(controller, 
                "http://localhost", 
                null, 
                null, 
                new NameValueCollection(), 
                new NameValueCollection(), 
                new HttpCookieCollection(),
                state);

			forum.Name = "ถ้า" + "engl";
			forum.Description = forum.Name + "... description.";
			forum.Category = GetACategory();

			var result = controller.Add(forum);
			Assert.IsTrue(result is RedirectToRouteResult);

			result = controller.Delete(forum.ShortName);
			Assert.IsTrue(result is RedirectToRouteResult);

            mockForumClient.VerifyAll();
		}

        [Test]
        public void TestRoutingInThai()
        {
            var routes = new RouteCollection();
            var routesConfig = RouteMappingConfiguration.Current;

            RoutingHelper.RegisterRoutes(routes, routesConfig);

            RoutingAssert.VirtualPathNotNull(routes, new
            {
                controller = "Forums",
                action = "Detail",
                forum = "ShortName"
            }); 
        }

        [Test]
        public void Forum_Add_Cyrillic()
        {
            var forum = new Forum();
            int userId = 10;
            User user = new User(userId, "UserName");
            SessionStateItemCollection state = ForumsControllerTest.GetSessionWithTestUser(user);
            var mockForumClient = new Mock<IForumsServiceClient>();
            mockForumClient.Setup(m => m.Add(forum, userId));
            var controller = new ForumsController(null, mockForumClient.Object);
            controller.ControllerContext = new FakeControllerContext(
                controller, "http://localhost", null, null, new NameValueCollection(), new NameValueCollection(), new HttpCookieCollection(), state);

            forum.Name = "Юнит тест форум ...";
            forum.Description = forum.Name + "... описание.";
            forum.Category = GetACategory();

            var result = controller.Add(forum);
            Assert.IsTrue(result is RedirectToRouteResult);

            mockForumClient.VerifyAll();
        }

        [Test]
        public void Forum_Delete_Cyrillic()
        {
            var forum = new Forum();
            //int userId = 10;
            //User user = new User(userId, "UserName");
            string shortName = "Short Name";
            SessionStateItemCollection state = ForumsControllerTest.GetSessionWithTestUser();
            var mockForumClient = new Mock<IForumsServiceClient>();
            mockForumClient.Setup(m => m.Delete(shortName));
            var controller = new ForumsController(null, mockForumClient.Object);
            controller.ControllerContext = new FakeControllerContext(
                controller, "http://localhost", null, null, new NameValueCollection(), new NameValueCollection(), new HttpCookieCollection(), state);

            forum.Name = "Юнит тест форум ...";
            forum.Description = forum.Name + "... описание.";
            forum.Category = GetACategory();
            forum.ShortName = shortName;

            var result = controller.Delete(forum.ShortName);
            Assert.IsTrue(result is RedirectToRouteResult);

            mockForumClient.VerifyAll();
        }

        [Test]
        public void Name_MixedSymbols()
        {
            string forumName = "Кирилица12Latin";
            User user = ServicesTests.GetTestUser();
            var forum = new Forum()
            {
                Name = forumName,
                Description = forumName + "descr",
                Category = GetACategory()
            };
            var mockForumServiceClient = new Mock<IForumsServiceClient>();
            mockForumServiceClient
                .Setup(m => m.Add(forum, user.Id));
            var controller = new ForumsController(null, mockForumServiceClient.Object);
            controller.ControllerContext = FakeControllerContext.Create(controller, "http://localhost", ForumsControllerTest.GetSessionWithTestUser(user));

            var result = controller.Add(forum);
            Assert.AreEqual(forumName, forum.Name);
            Assert.IsInstanceOf<RedirectToRouteResult>(result);

            mockForumServiceClient.VerifyAll();
        }

        [Test]
        public void ShortName()
        {
            const string forumName = "Кирилица12Latin";
            var forum = new Forum()
            {
                Name = forumName,
                Description = forumName + "descr",
                Category = GetACategory()
            };
            User user = ServicesTests.GetTestUser();
            var mockForumServiceClient = new Mock<IForumsServiceClient>();
            mockForumServiceClient
                .Setup(m => m.Add(forum, user.Id));
            var controller = new ForumsController(null, mockForumServiceClient.Object);
            controller.ControllerContext = FakeControllerContext.Create(controller, "http://localhost", ForumsControllerTest.GetSessionWithTestUser(user));
    
            ActionResult result = controller.Add(forum);
            Assert.AreEqual(forum.ShortName, forumName);
            Assert.IsInstanceOf<RedirectToRouteResult>(result);
            Assert.That(((RedirectToRouteResult)result).RouteValues["action"], 
                Is.Not.Null.And.EqualTo("Detail"));
            Assert.That(((RedirectToRouteResult)result).RouteValues["forum"],
                Is.Not.Null.And.EqualTo(forumName));

            mockForumServiceClient.VerifyAll();
        }
	}
}